﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Drawing;

//TODO: implement completely and test

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// 
    /// </summary>
    public class Logarithmic : BaseImageFilter
    {
        private bool m_bConvertToGrayscale = false;

        //TODO: check if this can be deleted (inheritance)
        //protected float m_fScale = 1.0f;
        protected float m_fNumber = -1.0f;

        protected bool m_bUseBase10 = true;

        /// <summary>
        /// Initializes a new instance of the <see cref="Logarithmic"/> class.
        /// </summary>
        public Logarithmic()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Logarithmic"/> class.
        /// </summary>
        /// <param name="_bConvertToGrayScale">if set to <c>true</c> [_b convert to gray scale].</param>
        public Logarithmic(bool _bConvertToGrayScale)
        {
            m_bConvertToGrayscale = _bConvertToGrayScale;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Logarithmic"/> class.
        /// </summary>
        /// <param name="_logarithmic">The _logarithmic.</param>
        internal Logarithmic(Logarithmic _logarithmic)
        {
            this.m_bConvertToGrayscale = _logarithmic.m_bConvertToGrayscale;
            this.m_bUseBase10 = _logarithmic.m_bUseBase10;
            this.m_fNumber = _logarithmic.m_fNumber;
            this.m_fOffset = _logarithmic.m_fOffset;
            this.m_fScale = _logarithmic.m_fScale;
        }

        /// <summary>
        /// Gets or sets a value indicating whether [use base10].
        /// </summary>
        /// <value><c>true</c> if [use base10]; otherwise, <c>false</c>.</value>
        public bool UseBase10
        {
            get { return m_bUseBase10; }
            set { m_bUseBase10 = value; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether [convert to gray scale].
        /// </summary>
        /// <value><c>true</c> if [convert to gray scale]; otherwise, <c>false</c>.</value>
        public bool ConvertToGrayScale
        {
            get { return m_bConvertToGrayscale; }
            set { m_bConvertToGrayscale = value; }
        }

        /// <summary>
        /// Gets or sets the number.
        /// </summary>
        /// <value>The number.</value>
        public float Number
        {
            get { return m_fNumber; }
            set { m_fNumber = value; }
        }

        /// <summary>
        /// Execute this Filter
        /// A Debug Statement is written if a concrete Filter is called
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            ApplyLogarithmic(_bitmap, _bitmap.Width, _bitmap.Height, m_fNumber, m_fScale, m_fOffset);
            return _bitmap;
        }
        /// <summary>
        /// exponential algorithm..
        /// *a) assume the image is grey level (hence RR=GG=BB)
        /// b) use value 0x000000ff to get the BB value
        /// c) apply the operation (eg exponentiate).
        /// d) return the result
        /// </summary>
        /// <param name="_bitmap">The _bitmap.</param>
        /// <param name="nWidth">Width of the n.</param>
        /// <param name="nHeight">Height of the n.</param>
        /// <param name="_dLogNumber">The _d log number.</param>
        /// <param name="fScaleval">The f scaleval.</param>
        /// <param name="fOffsetval">The f offsetval.</param>
        private void ApplyLogarithmic(UnsafeBitmap _bitmap, int nWidth, int nHeight, double _dLogNumber,
                     float fScaleval, float fOffsetval)
        {
            int nResult = 0;

            UnsafeBitmap bitmap = _bitmap;

            Color color = Color.Transparent;
            int nR = -1;
            int nG = -1;
            int nB = -1;

            for (int y = 0; y < nHeight; y++)
            {
                for (int x = 0; x < nWidth; x++)
                {
                    //Select required bits from image
                    color = bitmap.GetPixel(x, y);
                    int nValue = color.ToArgb();

                    if (m_bConvertToGrayscale)
                    {
                        nValue &= 0x000000ff;
                    }
                    else
                    {
                        nR = GetColorLogarithmResult(color.R, _dLogNumber, fScaleval, fOffsetval);
                        nG = GetColorLogarithmResult(color.G, _dLogNumber, fScaleval, fOffsetval); ;
                        nB = GetColorLogarithmResult(color.B, _dLogNumber, fScaleval, fOffsetval); ;
                        color = Color.FromArgb(nR, nG, nB);
                    }

                    //Perform operation
                    nResult = (int)(Math.Log10(_dLogNumber));
                    nResult = (int)((nResult * fScaleval) + fOffsetval);
                    if (nResult > 255) nResult = 255;
                    if (nResult < 0) nResult = 0;

                    if (m_bConvertToGrayscale)
                    {
                        color = Color.FromArgb(((int)(uint)(0xFF << 24 | nResult << 16 | nResult << 8 | nResult)));
                    }
                    bitmap.SetPixel(x, y, color);
                }
            }
        }

        /// <summary>
        /// Gets the color logarithm result.
        /// </summary>
        /// <param name="_nChannel">The _n channel.</param>
        /// <param name="_dLog">The _d log.</param>
        /// <param name="fScaleVal">The f scale val.</param>
        /// <param name="fOffsetVal">The f offset val.</param>
        /// <returns></returns>
        private int GetColorLogarithmResult(int _nChannel, double _dLog, float fScaleVal, float fOffsetVal)
        {
            int nResult = (int)(Math.Log10(_dLog) * fScaleVal + fOffsetVal);
            if (nResult > 255) nResult = 255;
            if (nResult < 0) nResult = 0;
            return nResult;
        }

        /// <summary>
        /// This is used to clone a filter, each filter must implement it's own clone functionality
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new Logarithmic(this);
        }
    }
}
